import sys
import os
import toml
import tempfile
import subprocess
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                            QHBoxLayout, QLabel, QLineEdit, QPushButton, 
                            QGroupBox, QFileDialog, QMessageBox, QTextEdit,
                            QDialog)
from PyQt6.QtCore import Qt, QProcess
from PyQt6.QtGui import QFont, QIcon

class FrpConfig:
    """处理FRP配置的功能类"""
    def __init__(self):
        self.config = {
            'serverAddr': '',
            'serverPort': 0,
            'auth': {
                'token': ''
            },
            'proxies': [{
                'name': '汽车维修管理-TCP',
                'type': 'tcp',
                'localIP': '127.0.0.1',
                'localPort': 0,
                'remotePort': 0
            }]
        }

    def update_config(self, server_addr, server_port, token, local_port, remote_port):
        """更新配置内容"""
        self.config.update({
            'serverAddr': server_addr,
            'serverPort': int(server_port),
            'auth': {
                'token': token
            }
        })
        self.config['proxies'][0].update({
            'localPort': int(local_port),
            'remotePort': int(remote_port)
        })

    def get_config_str(self):
        """获取TOML格式的配置字符串"""
        # 创建一个新的配置字典，以确保正确的格式和顺序
        formatted_config = {
            'serverAddr': self.config['serverAddr'],
            'serverPort': self.config['serverPort'],
            'auth.token': self.config['auth']['token'],
            'proxies': self.config['proxies']
        }
        # 先生成TOML字符串
        config_str = toml.dumps(formatted_config)
        # 修正auth.token的格式（移除引号）
        config_str = config_str.replace('"auth.token"', 'auth.token')
        return config_str

    def save_config_to_file(self, file_path):
        """保存配置到文件"""
        # 创建正确格式的配置用于保存
        formatted_config = {
            'serverAddr': self.config['serverAddr'],
            'serverPort': self.config['serverPort'],
            'auth.token': self.config['auth']['token'],
            'proxies': self.config['proxies']
        }
        # 先生成TOML字符串
        config_str = toml.dumps(formatted_config)
        # 修正auth.token的格式（移除引号）
        config_str = config_str.replace('"auth.token"', 'auth.token')
        # 写入文件
        with open(file_path, 'w', encoding='utf-8') as configfile:
            configfile.write(config_str)

class FrpClientGUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.frp_config = FrpConfig()
        self.frpc_process = None
        self.is_running = False
        self.init_ui()

    def init_ui(self):
        """初始化UI"""
        self.setWindowTitle('FRP客户端配置工具')
        self.setGeometry(100, 100, 600, 700)
        self.setStyleSheet("""
            QMainWindow {
                background-color: #f0f2f5;
            }
            QGroupBox {
                background-color: white;
                border: none;
                border-radius: 8px;
                margin-top: 12px;
                font-weight: bold;
                padding: 15px;
                box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px 0 5px;
                color: #1a1a1a;
                font-size: 14px;
            }
            QPushButton {
                background-color: #1890ff;
                color: white;
                border: none;
                padding: 8px 20px;
                border-radius: 4px;
                min-width: 80px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #40a9ff;
            }
            QPushButton:pressed {
                background-color: #096dd9;
            }
            QLineEdit {
                padding: 8px;
                border: 1px solid #d9d9d9;
                border-radius: 4px;
                background-color: white;
                color: #000000;
                font-size: 13px;
                selection-background-color: #1890ff;
                selection-color: white;
            }
            QLineEdit:focus {
                border-color: #40a9ff;
                outline: none;
                background-color: #ffffff;
            }
            QLineEdit:disabled {
                background-color: #f5f5f5;
                color: #595959;
            }
            QLabel {
                color: #262626;
                font-size: 13px;
                font-weight: 500;
            }
            QTextEdit {
                border: 1px solid #d9d9d9;
                border-radius: 4px;
                padding: 5px;
                background-color: white;
                color: #000000;
                font-size: 13px;
            }
            QTextEdit:focus {
                border-color: #40a9ff;
            }
            #status_label {
                color: #595959;
                font-size: 13px;
                padding: 10px;
            }
            #title_label {
                color: #262626;
                font-size: 20px;
                font-weight: bold;
                padding: 10px 0;
            }
        """)

        # 创建中央部件和主布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)

        # 标题
        title_label = QLabel('FRP客户端配置工具')
        title_label.setObjectName("title_label")
        title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        main_layout.addWidget(title_label)

        # 服务器配置组
        self.create_server_group(main_layout)
        
        # 本地配置组
        self.create_local_group(main_layout)
        
        # FRPC配置组
        self.create_frpc_group(main_layout)
        
        # 按钮组
        self.create_button_group(main_layout)
        
        # 状态标签
        self.status_label = QLabel('状态: 未运行')
        self.status_label.setObjectName("status_label")
        self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        main_layout.addWidget(self.status_label)

    def create_server_group(self, parent_layout):
        """创建服务器配置组"""
        group = QGroupBox('服务器配置')
        layout = QVBoxLayout()

        # 服务器地址
        addr_layout = QHBoxLayout()
        addr_label = QLabel('服务器地址:')
        self.server_addr = QLineEdit()
        self.server_addr.setText('39.104.71.231')
        addr_layout.addWidget(addr_label)
        addr_layout.addWidget(self.server_addr)
        layout.addLayout(addr_layout)

        # 服务器端口
        port_layout = QHBoxLayout()
        port_label = QLabel('服务器监听端口:')
        self.server_port = QLineEdit()
        self.server_port.setText('7000')
        port_layout.addWidget(port_label)
        port_layout.addWidget(self.server_port)
        layout.addLayout(port_layout)

        # Token
        token_layout = QHBoxLayout()
        token_label = QLabel('Token:')
        self.token = QLineEdit()
        self.token.setText('adminabc123')
        token_layout.addWidget(token_label)
        token_layout.addWidget(self.token)
        layout.addLayout(token_layout)

        group.setLayout(layout)
        parent_layout.addWidget(group)

    def create_local_group(self, parent_layout):
        """创建本地配置组"""
        group = QGroupBox('本地配置')
        layout = QVBoxLayout()

        # 本地端口
        local_port_layout = QHBoxLayout()
        local_port_label = QLabel('本地端口:')
        self.local_port = QLineEdit()
        self.local_port.setText('5000')
        local_port_layout.addWidget(local_port_label)
        local_port_layout.addWidget(self.local_port)
        layout.addLayout(local_port_layout)

        # 远程端口
        remote_port_layout = QHBoxLayout()
        remote_port_label = QLabel('远程开放端口:')
        self.remote_port = QLineEdit()
        self.remote_port.setText('8002')
        remote_port_layout.addWidget(remote_port_label)
        remote_port_layout.addWidget(self.remote_port)
        layout.addLayout(remote_port_layout)

        group.setLayout(layout)
        parent_layout.addWidget(group)

    def create_frpc_group(self, parent_layout):
        """创建FRPC配置组"""
        group = QGroupBox('FRPC配置')
        layout = QHBoxLayout()

        frpc_label = QLabel('frpc路径:')
        self.frpc_path = QLineEdit()
        self.frpc_path.setText(self.get_frpc_path())
        select_button = QPushButton('选择frpc')
        select_button.clicked.connect(self.select_frpc)

        layout.addWidget(frpc_label)
        layout.addWidget(self.frpc_path)
        layout.addWidget(select_button)

        group.setLayout(layout)
        parent_layout.addWidget(group)

    def create_button_group(self, parent_layout):
        """创建按钮组"""
        button_layout = QHBoxLayout()
        
        load_btn = QPushButton('加载配置')
        generate_btn = QPushButton('生成配置')
        save_btn = QPushButton('保存配置')
        self.run_btn = QPushButton('运行')

        load_btn.clicked.connect(self.load_config)
        generate_btn.clicked.connect(self.generate_config)
        save_btn.clicked.connect(self.save_config)
        self.run_btn.clicked.connect(self.toggle_frpc)

        button_layout.addWidget(load_btn)
        button_layout.addWidget(generate_btn)
        button_layout.addWidget(save_btn)
        button_layout.addWidget(self.run_btn)

        parent_layout.addLayout(button_layout)

    def get_frpc_path(self):
        """获取frpc可执行文件路径"""
        return 'frpc.exe' if sys.platform == 'win32' else './frpc'

    def select_frpc(self):
        """选择frpc可执行文件"""
        file_types = "FRP Client (frpc.exe);;All Files (*.*)" if sys.platform == 'win32' else "FRP Client (frpc);;All Files (*.*)"
        file_path, _ = QFileDialog.getOpenFileName(self, "选择frpc可执行文件", "", file_types)
        if file_path:
            self.frpc_path.setText(file_path)

    def update_config_from_gui(self):
        """从GUI获取配置信息并更新到配置对象"""
        try:
            # 验证输入
            if not all([self.server_addr.text(), self.server_port.text(),
                       self.local_port.text(), self.remote_port.text()]):
                raise ValueError("所有字段都必须填写")

            # 验证端口号
            for port in [self.server_port.text(), self.local_port.text(), 
                        self.remote_port.text()]:
                port_num = int(port)
                if not (0 <= port_num <= 65535):
                    raise ValueError("端口号必须在0-65535之间")

            self.frp_config.update_config(
                self.server_addr.text(),
                self.server_port.text(),
                self.token.text(),
                self.local_port.text(),
                self.remote_port.text()
            )
        except ValueError as e:
            QMessageBox.critical(self, "错误", str(e))
            raise

    def generate_config(self):
        """生成配置预览"""
        try:
            self.update_config_from_gui()
            
            dialog = QDialog(self)
            dialog.setWindowTitle("配置预览")
            dialog.setGeometry(200, 200, 400, 300)
            
            layout = QVBoxLayout(dialog)
            
            text_edit = QTextEdit()
            text_edit.setPlainText(self.frp_config.get_config_str())
            layout.addWidget(text_edit)
            
            copy_btn = QPushButton("复制配置")
            copy_btn.clicked.connect(lambda: self.copy_to_clipboard(text_edit.toPlainText()))
            layout.addWidget(copy_btn)
            
            dialog.exec()
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"生成配置时出错：{str(e)}")

    def copy_to_clipboard(self, text):
        """复制文本到剪贴板"""
        QApplication.clipboard().setText(text)
        QMessageBox.information(self, "成功", "配置已复制到剪贴板")

    def save_config(self):
        """保存配置到文件"""
        try:
            self.update_config_from_gui()
            
            file_path, _ = QFileDialog.getSaveFileName(
                self,
                "保存配置文件",
                "frpc.toml",
                "TOML files (*.toml);;All Files (*.*)"
            )
            
            if file_path:
                self.frp_config.save_config_to_file(file_path)
                QMessageBox.information(self, "成功", f"配置已保存到 {file_path}")
                
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存配置文件时出错：{str(e)}")

    def load_config(self):
        """加载配置文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "选择配置文件",
            "",
            "TOML files (*.toml);;All Files (*.*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    config = toml.load(f)
                    
                self.server_addr.setText(config['serverAddr'])
                self.server_port.setText(str(config['serverPort']))
                self.token.setText(config.get('auth.token', ''))
                
                if 'proxies' in config and len(config['proxies']) > 0:
                    proxy = config['proxies'][0]
                    self.local_port.setText(str(proxy['localPort']))
                    self.remote_port.setText(str(proxy['remotePort']))
                
                QMessageBox.information(self, "成功", f"已加载配置文件：{file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"加载配置文件时出错：{str(e)}")

    def toggle_frpc(self):
        """切换frpc运行状态"""
        if not self.is_running:
            self.start_frpc()
        else:
            self.stop_frpc()

    def start_frpc(self):
        """启动frpc"""
        try:
            # 创建临时配置文件
            config_path = self.create_temp_config()
            if not config_path:
                return

            frpc_path = self.frpc_path.text()
            
            # 检查frpc是否存在
            if not os.path.exists(frpc_path):
                QMessageBox.critical(self, "错误", f"找不到frpc��序：{frpc_path}")
                return

            # 启动frpc进程
            self.frpc_process = QProcess()
            self.frpc_process.readyReadStandardError.connect(self.handle_error)
            self.frpc_process.readyReadStandardOutput.connect(self.handle_output)
            self.frpc_process.finished.connect(self.on_process_finished)
            self.frpc_process.start(frpc_path, ['-c', config_path])

            self.is_running = True
            self.run_btn.setText("停止")
            self.status_label.setText("状态: 运行中")
            
            # 使用统一的消息框样式
            self.show_styled_message(
                "连接成功",
                f"服务器连接信息：\n"
                f"地址：{self.server_addr.text()}:{self.remote_port.text()}\n\n"
                f"可以通过访问 {self.server_addr.text()}:{self.remote_port.text()} 来连接您的本地服务。"
            )
            
        except Exception as e:
            self.show_styled_message(
                "错误",
                f"启动frpc失败：{str(e)}",
                QMessageBox.Icon.Critical
            )

    def handle_error(self):
        """处理frpc的错误输出"""
        try:
            error = self.frpc_process.readAllStandardError().data().decode('utf-8', errors='ignore')
            if error.strip():  # 只有当有实际错误信息时才显示
                self.last_error = error  # 保存最后的错误信息
                self.show_styled_message("frpc错误", error, QMessageBox.Icon.Warning)
        except Exception as e:
            print(f"处理错误输出时发生异常: {e}")

    def handle_output(self):
        """处理frpc的标准输出"""
        try:
            output = self.frpc_process.readAllStandardOutput().data().decode('utf-8', errors='ignore')
            if output.strip():
                print("frpc输出:", output)
        except Exception as e:
            print(f"处理标准输出时发生异常: {e}")

    def create_temp_config(self):
        """创建临时配置文件"""
        try:
            self.update_config_from_gui()
            temp_config = tempfile.NamedTemporaryFile(
                mode='w',
                suffix='.toml',
                delete=False,
                encoding='utf-8'
            )
            self.frp_config.save_config_to_file(temp_config.name)
            return temp_config.name
        except Exception as e:
            QMessageBox.critical(self, "错误", f"创建配置文件失败：{str(e)}")
            return None

    def stop_frpc(self):
        """停止frpc"""
        if self.frpc_process:
            try:
                # 断开所有信号连接，避免触发回调
                self.frpc_process.readyReadStandardError.disconnect()
                self.frpc_process.readyReadStandardOutput.disconnect()
                self.frpc_process.finished.disconnect()
            except:
                pass

            # 尝试正常终止进程
            self.frpc_process.terminate()
            
            # 使用较长的超时时间，确保进程有足够时间清理
            if self.frpc_process.state() != QProcess.ProcessState.NotRunning:
                if not self.frpc_process.waitForFinished(2000):  # 等待2秒
                    print("进程未能正常终止，强制结束")
                    self.frpc_process.kill()  # 强制结束
                    self.frpc_process.waitForFinished(1000)  # 等待强制结束完成
            
            # 确保进程已经完全终止后再清理资源
            if self.frpc_process.state() == QProcess.ProcessState.NotRunning:
                self.frpc_process = None
                self.is_running = False
                self.run_btn.setText("运行")
                self.status_label.setText("状态: 已停止")
            else:
                print("警告：进程可能未完全终止")

    def closeEvent(self, event):
        """窗口关闭时的处理"""
        if self.is_running and self.frpc_process:
            self.stop_frpc()
        event.accept()

    def on_process_finished(self, exit_code, exit_status):
        """frpc进程结束的处理"""
        if not self.is_running:  # 如果是手动停止的，就不显示错误
            return
            
        if exit_code != 0:
            try:
                # 尝试读取所有剩余的错误输出
                error = ""
                if self.frpc_process:
                    error = self.frpc_process.readAllStandardError().data().decode('utf-8', errors='ignore')
                
                # 如果没有直接的错误输出，使用之前保存的错误信息
                if not error.strip() and hasattr(self, 'last_error'):
                    error = self.last_error

                error_message = f"frpc异常退出，退出码：{exit_code}"
                if error.strip():
                    error_message += f"\n错误信息：{error}"

                self.show_styled_message(
                    "警告", 
                    error_message,
                    QMessageBox.Icon.Warning
                )
            except Exception as e:
                print(f"处理进程结束错误时发生异常: {e}")
                self.show_styled_message(
                    "警告",
                    f"frpc异常退出，退出码：{exit_code}",
                    QMessageBox.Icon.Warning
                )
        
        self.frpc_process = None
        self.is_running = False
        self.run_btn.setText("运行")
        self.status_label.setText("状态: 已停止")

    def show_styled_message(self, title, message, icon=QMessageBox.Icon.Information):
        """显示统一样式的消息框"""
        msg_box = QMessageBox(self)
        msg_box.setWindowTitle(title)
        msg_box.setText(message)
        msg_box.setIcon(icon)
        
        # 设置消息框样式
        msg_box.setStyleSheet("""
            QMessageBox {
                background-color: white;
            }
            QMessageBox QLabel {
                color: #262626;
                font-size: 14px;
                min-width: 300px;
            }
            QPushButton {
                background-color: #1890ff;
                color: white;
                border: none;
                padding: 8px 20px;
                border-radius: 4px;
                min-width: 80px;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #40a9ff;
            }
            QPushButton:pressed {
                background-color: #096dd9;
            }
        """)
        
        return msg_box.exec()

def main():
    app = QApplication(sys.argv)
    window = FrpClientGUI()
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main() 